﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Galaktika.BI.Runtime.Services;
using Galaktika.BI.Silverlight.Controls.General.Tree;
using Ranet.AgOlap.Core.MetadataDescription;
using Galaktika.BI.Silverlight.Commands;
using System.Threading;
using Galaktika.BI.Silverlight.Controls.General;
using Galaktika.BI.Silverlight.Controls.General.ClientServer;

namespace Galaktika.BI.Silverlight.Controls.LevelChoice
{
    public partial class LevelChoiceControl : UserControl
    {
        public LevelChoiceControl()
        {
            InitializeComponent();
        }

        public IBindingContext Context { get; set; }

        #region Свойства для настройки на OLAP
        /// <summary>
        /// 
        /// </summary>
        private String m_Connection = String.Empty;
        /// <summary>
        /// 
        /// </summary>
        public String Connection
        {
            get
            {
                return m_Connection;
            }
            set
            {
                m_Connection = value;
            }
        }

        /// <summary>
        /// Имя OLAP куба
        /// </summary>
        String m_CubeName = String.Empty;
        /// <summary>
        /// Имя OLAP куба
        /// </summary>
        public String CubeName
        {
            get
            {
                return this.m_CubeName;
            }
            set
            {
                m_CubeName = value;
            }
        }

        public String m_DimensionUniqueName = String.Empty;
        /// <summary>
        /// Имя OLAP измерения
        /// </summary>
        public String DimensionUniqueName
        {
            get
            {
                return m_DimensionUniqueName;
            }
            set
            {
                m_DimensionUniqueName = value;
            }
        }

        public String m_HierarchyUniqueName = String.Empty;
        /// <summary>
        /// Имя иерархии OLAP измерения
        /// </summary>
        public String HierarchyUniqueName
        {
            get
            {
                return m_HierarchyUniqueName;
            }
            set
            {
                m_HierarchyUniqueName = value;
            }
        }
        #endregion Свойства для настройки на OLAP

        private LevelInfo m_SelectedInfo = null;
        public LevelInfo SelectedInfo
        {
            get
            {
                return m_SelectedInfo;
            }
        }

        /// <summary>
        /// Событие генерируется после окончания выбора
        /// </summary>
        public event EventHandler ApplySelection;

        /// <summary>
        /// Событие генерируется при нажатии на кнопку Отмена
        /// </summary>
        public event EventHandler CancelSelection;

        /// <summary>
        /// Генерирует событие "Выбор окончен"
        /// </summary>
        private void Raise_ApplySelection()
        {
            EventHandler handler = ApplySelection;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }

        /// <summary>
        /// Генерирует событие "Отмена"
        /// </summary>
        private void Raise_CancelSelection()
        {
            EventHandler handler = CancelSelection;
            if (handler != null)
                handler(this, EventArgs.Empty);
        }

        public void Initialize()
        {
            RefreshTree();
        }

        private void RefreshTree()
        {
            Tree.Items.Clear();

            //Если имя иерархии задано, то отображать в дереве нужно только эту иерархию
            if (!String.IsNullOrEmpty(HierarchyUniqueName) && !String.IsNullOrEmpty(DimensionUniqueName))
            {
                CreateRootHierarchyNode();
            }
            else
            {
                //Если задано имя измерения, то нужно отображать информацию только для этого измерения
                if (!String.IsNullOrEmpty(DimensionUniqueName))
                {
                    CreateRootDimensionNode();
                }
                else
                {
                    CreateRootCubeNode();
                }
            }
        }

        #region Корневой узел для куба
        void CreateRootCubeNode()
        {
            //Будем выводить информацию для всего куба
            CustomTreeNode cubeNode = new CustomTreeNode();
            cubeNode.Text = CubeName;
            cubeNode.Icon = UriResources.Images.Cube16;
            Tree.Items.Add(cubeNode);
            cubeNode.Expanded += new RoutedEventHandler(cubeNode_Expanded);
            cubeNode.IsWaiting = true;
            cubeNode.IsExpanded = true;
        }

        void cubeNode_Expanded(object sender, RoutedEventArgs e)
        {
            CustomTreeNode cubeNode = sender as CustomTreeNode;
            if (cubeNode != null && !cubeNode.IsInitialized)
            {
                IUserTaskManager taskManager = (IUserTaskManager)Context.Services.GetService(typeof(IUserTaskManager));
                if (taskManager != null)
                {
                    InvokeSchema schema = CommandBuilder.CreateGetDimensionsSchema((string)this.Context.UserData["_$Id"], m_Connection, m_CubeName);
                    cubeNode.IsWaiting = true;

                    taskManager.ProcessTaskItem(Localization.AppTask_DownloadingDimensions,
                        new WaitCallback(InvokeCommandBySchema),
                        new ChoiceControlInvokeSchemaWrapper(schema, cubeNode));
                }
            }
        }

        void GetDimensions_InvokeCommandCompleted(InvokeResultSchema res, CustomTreeNode parentNode)
        {
            List<DimensionInfo> dimensions = XmlUtility.XmlStr2Obj<List<DimensionInfo>>(res.Content);
            if (dimensions != null)
            {
                foreach (DimensionInfo info in dimensions)
                {
                    if (info.DimensionType != DimensionInfoTypeEnum.Measure)
                    {
                        DimensionTreeNode dimNode = new DimensionTreeNode(info);
                        dimNode.IsWaiting = true;
                        dimNode.Expanded += new RoutedEventHandler(dimNode_Expanded);
                        if (parentNode == null)
                            Tree.Items.Add(dimNode);
                        else
                            parentNode.Items.Add(dimNode);
                    }
                }
            }
        }
        #endregion Корневой узел для куба

        #region Корневой узел для иерархии
        private void CreateRootHierarchyNode()
        {
            IUserTaskManager taskManager = (IUserTaskManager)Context.Services.GetService(typeof(IUserTaskManager));
            if (taskManager != null)
            {
                InvokeSchema schema = CommandBuilder.CreateGetHierarchySchema((string)this.Context.UserData["_$Id"], Connection, CubeName, DimensionUniqueName, HierarchyUniqueName);
                Tree.IsWaiting = true;

                taskManager.ProcessTaskItem(Localization.AppTask_DownloadingHierarchy,
                    new WaitCallback(InvokeCommandBySchema),
                    new ChoiceControlInvokeSchemaWrapper(schema));
            }
        }

        void GetHierarchy_InvokeCommandCompleted(InvokeResultSchema res, CustomTreeNode parentNode)
        {
            HierarchyInfo hierarchy = XmlUtility.XmlStr2Obj<HierarchyInfo>(res.Content);
            if (hierarchy != null)
            {
                HierarchyTreeNode dimNode = new HierarchyTreeNode(hierarchy);
                dimNode.IsWaiting = true;
                dimNode.Expanded += new RoutedEventHandler(hierarchyNode_Expanded);
                Tree.Items.Add(dimNode);
            }
        }
        #endregion Корневой узел для иерархии

        #region Корневой узел для измерения
        private void CreateRootDimensionNode()
        {
            IUserTaskManager taskManager = (IUserTaskManager)Context.Services.GetService(typeof(IUserTaskManager));
            if (taskManager != null)
            {
                InvokeSchema schema = CommandBuilder.CreateGetDimensionSchema((string)this.Context.UserData["_$Id"], Connection, CubeName, DimensionUniqueName);
                Tree.IsWaiting = true;

                taskManager.ProcessTaskItem(Localization.AppTask_DownloadingDimension,
                    new WaitCallback(InvokeCommandBySchema),
                    new ChoiceControlInvokeSchemaWrapper(schema));
            }
        }

        void GetDimension_InvokeCommandCompleted(InvokeResultSchema res, CustomTreeNode parentNode)
        {
            DimensionInfo dimension = XmlUtility.XmlStr2Obj<DimensionInfo>(res.Content);
            if (dimension != null)
            {
                DimensionTreeNode dimNode = new DimensionTreeNode(dimension);
                dimNode.IsWaiting = true;
                dimNode.Expanded += new RoutedEventHandler(dimNode_Expanded);
                Tree.Items.Add(dimNode);
            }
        }
        #endregion Корневой узел для измерения

        private void InvokeCommandBySchema(object state)
        {
            ChoiceControlInvokeSchemaWrapper wrapper = state as ChoiceControlInvokeSchemaWrapper;
            if (wrapper != null)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(InvokeCommand), wrapper);
            }
        }

        void InvokeCommand(object state)
        {
            ChoiceControlInvokeSchemaWrapper wrapper = state as ChoiceControlInvokeSchemaWrapper;
            if (wrapper != null)
            {
                InvokeSchema schema = wrapper.Schema;
                using (SyncDataService ds = new SyncDataService())
                {
                    InvokeResultSchema res = ds.InvokeCommand(schema);

                    this.Dispatcher.BeginInvoke(delegate { StopWaiting(wrapper.UserState as CustomTreeNode); });

                    if (ds.Error != null)
                    {
                        LogException(ds.Error);
                        return;
                    }

                    String str = wrapper.Schema[KnownInvokeArgs.MetadataQueryInfo];
                    MetadataQueryArgs args = XmlUtility.XmlStr2Obj<MetadataQueryArgs>(str);
                    switch(args.QueryType)
                    {
                        case MetadataQueryType.GetDimensions:
                            this.Dispatcher.BeginInvoke(delegate { GetDimensions_InvokeCommandCompleted(res, wrapper.UserState as CustomTreeNode); });
                            break;
                        case MetadataQueryType.GetHierarchy:
                            this.Dispatcher.BeginInvoke(delegate { GetHierarchy_InvokeCommandCompleted(res, wrapper.UserState as CustomTreeNode); });
                            break;
                        case MetadataQueryType.GetDimension:
                            this.Dispatcher.BeginInvoke(delegate { GetDimension_InvokeCommandCompleted(res, wrapper.UserState as CustomTreeNode); });
                            break;
                        case MetadataQueryType.GetHierarchies:
                            this.Dispatcher.BeginInvoke(delegate { GetHierarchies_InvokeCommandCompleted(res, wrapper.UserState as CustomTreeNode); });
                            break;
                        case MetadataQueryType.GetLevels:
                            this.Dispatcher.BeginInvoke(delegate { GetLevels_InvokeCommandCompleted(res, wrapper.UserState as CustomTreeNode); });
                            break;
                    }
                }
            }
        }

        void StopWaiting(CustomTreeNode parentNode)
        {
            if (parentNode != null)
            {
                parentNode.IsWaiting = false;
            }
            else
            {
                Tree.IsWaiting = false;
            }
        }

        private bool LogException(Exception ex)
        {
            IOutputService log = (IOutputService)Context.Services.GetService(typeof(IOutputService));
            if (log != null)
            {
                log.WriteException(ex);
                return true;
            }
            return false;
        }

        void dimNode_Expanded(object sender, RoutedEventArgs e)
        {
            DimensionTreeNode dimNode = sender as DimensionTreeNode;
            if (dimNode != null && !dimNode.IsInitialized)
            {
                GetHierarchies(dimNode);
            }
        }

        private void GetHierarchies(DimensionTreeNode node)
        {
            String dimUniqueName = String.Empty;
            if (node != null)
            {
                dimUniqueName = node.Info.UniqueName;
            }

            IUserTaskManager taskManager = (IUserTaskManager)Context.Services.GetService(typeof(IUserTaskManager));
            if (taskManager != null)
            {
                InvokeSchema schema = CommandBuilder.CreateGetHierarchiesSchema((string)this.Context.UserData["_$Id"], Connection, CubeName, dimUniqueName);
                if (node != null)
                {
                    node.IsWaiting = true;
                }

                taskManager.ProcessTaskItem(Localization.AppTask_DownloadingHierarchies,
                    new WaitCallback(InvokeCommandBySchema),
                    new ChoiceControlInvokeSchemaWrapper(schema, node));
            }
        }

        void GetHierarchies_InvokeCommandCompleted(InvokeResultSchema res, CustomTreeNode parentNode)
        {
            List<HierarchyInfo> hierarchies = XmlUtility.XmlStr2Obj<List<HierarchyInfo>>(res.Content);
            if (hierarchies != null)
            {
                foreach (HierarchyInfo info in hierarchies)
                {
                    HierarchyTreeNode hierarchyNode = new HierarchyTreeNode(info);
                    hierarchyNode.IsWaiting = true;
                    hierarchyNode.Expanded += new RoutedEventHandler(hierarchyNode_Expanded);
                    if (parentNode == null)
                        Tree.Items.Add(hierarchyNode);
                    else
                        parentNode.Items.Add(hierarchyNode);

                }
            }
        }

        void hierarchyNode_Expanded(object sender, RoutedEventArgs e)
        {
            HierarchyTreeNode hierarchyNode = sender as HierarchyTreeNode;
            if (hierarchyNode != null && !hierarchyNode.IsInitialized)
            {
                GetLevels(hierarchyNode);
            }
        }

        private void GetLevels(HierarchyTreeNode node)
        {
            String dimUniqueName = String.Empty;
            String hierarchyUniqueName = String.Empty;
            if (node != null)
            {
                dimUniqueName = node.Info.ParentDimension;
                hierarchyUniqueName = node.Info.UniqueName;
            }

            IUserTaskManager taskManager = (IUserTaskManager)Context.Services.GetService(typeof(IUserTaskManager));
            if (taskManager != null)
            {
                InvokeSchema schema = CommandBuilder.CreateGetLevelsSchema((string)this.Context.UserData["_$Id"], Connection, CubeName, dimUniqueName, hierarchyUniqueName);
                if (node != null)
                {
                    node.IsWaiting = true;
                }

                taskManager.ProcessTaskItem(Localization.AppTask_DownloadingLevels,
                    new WaitCallback(InvokeCommandBySchema),
                    new ChoiceControlInvokeSchemaWrapper(schema, node));
            }
        }

        void GetLevels_InvokeCommandCompleted(InvokeResultSchema res, CustomTreeNode parentNode)
        {
            List<LevelInfo> levels = XmlUtility.XmlStr2Obj<List<LevelInfo>>(res.Content);
            if (levels != null)
            {
                int indx = 0;
                bool useAllLevel = true;
                foreach (LevelInfo info in levels)
                {
                    LevelTreeNode levelNode = new LevelTreeNode(info);
                    levelNode.Expanded += new RoutedEventHandler(node_Expanded);
                    levelNode.Collapsed += new RoutedEventHandler(node_Collapsed);

                    //Если нулевой уровень не All, то иконку ставим как для уровня 1
                    if (indx == 0 && info.LevelType != LevelInfoTypeEnum.All)
                        useAllLevel = false;
                    if (!useAllLevel)
                        levelNode.UseAllLevelIcon = false;

                    if (parentNode == null)
                        Tree.Items.Add(levelNode);
                    else
                        parentNode.Items.Add(levelNode);
                    indx++;
                }
            }
        }

        void node_Collapsed(object sender, RoutedEventArgs e)
        {
            ApplyItemsSelection();
            Raise_ApplySelection();
        }

        void node_Expanded(object sender, RoutedEventArgs e)
        {
            ApplyItemsSelection();
            Raise_ApplySelection();
        }

        private void ApplyItemsSelection()
        {
            LevelTreeNode node = null;
            node = Tree.SelectedItem as LevelTreeNode;

            if (node != null)
            {
                //Запоминаем выбранный элемент
                m_SelectedInfo = node.Info;
            }
        }
    }
}
